
package com.huaweicloud.saaslanding.install.applicationservice.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huaweicloud.saaslanding.basic.database.mybatis.conditions.query.QueryWrap;
import com.huaweicloud.saaslanding.basic.enumeration.TenantAppDomainStatusEnum;
import com.huaweicloud.saaslanding.basic.utils.CertificateUtil;
import com.huaweicloud.saaslanding.basic.utils.EncryptionUtil;
import com.huaweicloud.saaslanding.dao.common.entity.app.AppTemplate;
import com.huaweicloud.saaslanding.dao.common.entity.cloudservice.IamAccountInfo;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.WafHostResource;
import com.huaweicloud.saaslanding.dao.common.entity.tenant.TenantAppDomain;
import com.huaweicloud.saaslanding.dao.common.entity.tenant.TenantCertificate;
import com.huaweicloud.saaslanding.dao.common.repository.app.AppTemplateBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.cloudservice.IamAccountInfoBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.WafHostResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.tenant.TenantAppDomainBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.tenant.TenantCertificateBaseMapper;
import com.huaweicloud.saaslanding.huawei.iam.api.IamApi;
import com.huaweicloud.saaslanding.huawei.obs.ObsApi;
import com.huaweicloud.saaslanding.huawei.obs.constant.ObsConstant;
import com.huaweicloud.saaslanding.huawei.obs.dto.ObsAuthDto;
import com.huaweicloud.saaslanding.huawei.obs.dto.ObsFileInfoDto;
import com.huaweicloud.saaslanding.huawei.obs.enumeration.FileSizeEnum;
import com.huaweicloud.saaslanding.huawei.obs.enumeration.FileTypeEnum;
import com.huaweicloud.saaslanding.huawei.waf.api.CertificateApi;
import com.huaweicloud.saaslanding.huawei.waf.api.CloudProtectionDomainApi;
import com.huaweicloud.saaslanding.huawei.waf.request.CreateOrUpdateCertificateRequest;
import com.huaweicloud.saaslanding.huawei.waf.request.CreateProtectionDomainRequest;
import com.huaweicloud.saaslanding.huawei.waf.response.CertificateCreateResponse;
import com.huaweicloud.saaslanding.huawei.waf.response.CloudWafHostCreateResponse;
import com.huaweicloud.saaslanding.huawei.waf.util.WafNameUtil;
import com.huaweicloud.saaslanding.install.applicationservice.WafService;
import com.huaweicloud.saaslanding.install.dto.SaltDto;
import com.huaweicloud.saaslanding.install.properties.Elb;
import com.huaweicloud.saaslanding.install.properties.Endpoint;
import com.huaweicloud.saaslanding.install.properties.Iam;
import com.huaweicloud.saaslanding.install.properties.Obs;
import com.huaweicloud.saaslanding.install.repository.WorkSecretKeyMapper;
import com.huaweicloud.saaslanding.install.util.DomainNameUtil;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * WAF防火墙
 *
 * @since 2023-03-28
 */
@Slf4j
@Service
public class WafServiceImpl implements WafService {

    /**
     * 访问后端缓存规则设置的时间
     */
    private static final String CERTIFICATE = "WafCertificate.pem";

    /**
     * 访问后端缓存规则设置的优先级
     */
    private static final String CERTIFICATE_KEY = "WafCertificateKey.key";

    /**
     * LB负载均衡算法：Round Robin
     */
    private static final String ROUND_ROBIN_LB_ALGORITHM = "round_robin";

    /**
     * 付费方式：postPaid（按需）
     */
    private static final String POST_PAID = "postPaid";

    @Resource
    private Iam iam;

    @Resource
    private Endpoint endpoint;

    @Resource
    private Obs obs;

    @Resource
    private Elb elb;

    @Resource
    private ObsApi obsApi;

    @Resource
    private CertificateApi certificateApi;

    @Resource
    private CloudProtectionDomainApi cloudProtectionDomainApi;

    @Resource
    private TenantCertificateBaseMapper tenantCertificateBaseMapper;

    @Value("${saaslanding.platformAppName}")
    private String platformAppName;

    @Value("${saaslanding-install.waf.enabled:false}")
    private Boolean wafEnabled;

    @Value("${saaslanding-install.ssl.enabled:false}")
    private Boolean sslEnabled;

    @Value("${saaslanding-install.domain.certificate}")
    private String certificate;

    @Value("${saaslanding-install.domain.certificate-key}")
    private String certificateKey;

    private String projectId;

    private String enterpriseProjectId;

    private Map<String, String> headers;

    @Resource
    private IamAccountInfoBaseMapper iamAccountInfoBaseMapper;

    @Resource
    private WorkSecretKeyMapper workSecretKeyMapper;

    @Resource
    private WafHostResourceBaseMapper wafHostResourceBaseMapper;

    @Resource
    private TenantAppDomainBaseMapper tenantAppDomainBaseMapper;

    @Resource
    private AppTemplateBaseMapper appTemplateBaseMapper;

    @Override
    public Long createCertificate() {

        if (!sslEnabled || !wafEnabled) {
            log.info("----------------SSL或WAF未开启，创建WAF证书跳过---------------");
            return null;
        }

        log.info("----------------start：创建WAF证书---------------");
        commonConfig();

        CreateOrUpdateCertificateRequest request = new CreateOrUpdateCertificateRequest();
        request.setName(generateCertificateName())
            .setContent(certificate)
            .setKey(certificateKey);

        CertificateCreateResponse response =
            certificateApi.create(endpoint.getWaf(), headers, projectId, enterpriseProjectId, request);

        // 上传证书到OBS
        Long tenantCertificateId = uploadCertificateToObs(response.getId());
        log.info("----------------end：创建WAF证书--------------");

        return tenantCertificateId;
    }

    @Override
    public void createCloudProtectionDomain(Long tenantCertificateId) {
        if (!wafEnabled) {
            log.info("----------------WAF未开启，创建WAF防护域名跳过---------------");
            return;
        }

        log.info("----------------start：创建WAF防护域名---------------");
        commonConfig();

        CreateProtectionDomainRequest request = CreateProtectionDomainRequest.builder()
            .proxy(true)
            .server(WafNameUtil.getCloudWafServer(sslEnabled, elb.getElbEip()))
            .hostname(WafNameUtil.generateUniversalHostName(DomainNameUtil.getDomainName()))
            .lbAlgorithm(ROUND_ROBIN_LB_ALGORITHM)
            .http2Enable(false)
            .ipv6Enable(false)
            .paidType(POST_PAID)
            .build();

        // 若开启SSL，则需要关联证书
        if (sslEnabled) {
            TenantCertificate tenantCertificate = tenantCertificateBaseMapper.selectOne(
                new LambdaQueryWrapper<TenantCertificate>().eq(TenantCertificate::getTenantCode, platformAppName)
                    .eq(TenantCertificate::getIsDel, false));

            request.setCertificateid(tenantCertificate.getCloudCertificateId())
                .setCertificatename(tenantCertificate.getName());
        }

        // 创建waf防护域名
        CloudWafHostCreateResponse response =
            cloudProtectionDomainApi.create(endpoint.getWaf(), headers, projectId, enterpriseProjectId, request);

        WafHostResource wafHostResource =
            new WafHostResource().setHostName(WafNameUtil.generateUniversalHostName(DomainNameUtil.getDomainName()))
                .setTenantCertificateId(tenantCertificateId)
                .setCloudWafId(response.getId())
                .setCertificateId(response.getCertificateid())
                .setCname(WafNameUtil.generateCname(response.getAccessCode()))
                .setTenantCode(platformAppName)
                .setProtectStatus(response.getProtectStatus())
                .setIsProxy(response.getProxy());
        wafHostResourceBaseMapper.insert(wafHostResource);

        log.info("----------------end：创建WAF防护域名---------------");

        updateTenantAppDomain(tenantCertificateId, wafHostResource.getId());

    }

    @Override
    public void deleteDomain() {
        if (!wafEnabled) {
            log.info("----------------WAF未开启，删除防护域名跳过---------------");
            return;
        }

        log.info("----------------start：删除WAF防护域名---------------");
        List<WafHostResource> wafHostResources = wafHostResourceBaseMapper
            .selectList(new QueryWrap<WafHostResource>().lambda().eq(WafHostResource::getIsDel, false));

        Map<String, String> iamHeaders = IamApi.getIamHeader(iam.getEndpoint(), iam.getIamDomain(), iam.getIamUser(),
            iam.getIamPassword(), iam.getProjectId());

        for (WafHostResource resource : wafHostResources) {
            if (StringUtils.isNotBlank(resource.getCloudWafId())) {
                cloudProtectionDomainApi.delete(endpoint.getWaf(), iamHeaders, iam.getProjectId(),
                    iam.getEnterpriseProjectId(), resource.getCloudWafId());
            }

            if (StringUtils.isNotBlank(resource.getCertificateId())) {
                certificateApi.delete(endpoint.getWaf(), iamHeaders, iam.getProjectId(), iam.getEnterpriseProjectId(),
                    resource.getCertificateId());
            }
        }

        log.info("----------------end：删除CDN加速域名---------------");
    }

    /**
     * 生成证书名称
     *
     * @return 证书名称
     */
    @NotNull
    private String generateCertificateName() {
        return platformAppName.concat("证书");
    }

    /**
     * 字符串转文件
     *
     * @param content 字符串内容
     * @param fileName 文件名
     * @return 文件
     */
    private MultipartFile stringToMultipartFile(String content, String fileName) {
        byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
        return new MockMultipartFile(fileName, fileName, null, contentBytes);
    }

    /**
     * 上传证书到obs
     *
     * @param cloudCertificateId-华为云创建证书后返回的云证书ID
     */
    private Long uploadCertificateToObs(String cloudCertificateId) {
        ObsAuthDto obsAuthDto = ObsAuthDto.builder()
            .ak(iam.getAccessKey())
            .sk(iam.getSecretKey())
            .endpoint(endpoint.getObs())
            .bucketName(obs.getBucketName())
            .build();

        // 证书路径（database/certificate/tenantCode）
        String wafPrefix = platformAppName.concat(ObsConstant.CERTIFICATE)
            .concat(ObsConstant.OBS_FILE_DELIMITER)
            .concat(platformAppName);

        MultipartFile certificateFile = stringToMultipartFile(certificate, CERTIFICATE);
        ObsFileInfoDto certificateObsFileInfoDto = obsApi.safeUpload(wafPrefix, certificateFile, obsAuthDto,
            FileTypeEnum.CERTIFICATE, FileSizeEnum.MEGABYTE, 1);

        MultipartFile certificateKeyFile = stringToMultipartFile(certificateKey, CERTIFICATE_KEY);
        ObsFileInfoDto certificateKeyObsFileInfoDto = obsApi.safeUpload(wafPrefix, certificateKeyFile, obsAuthDto,
            FileTypeEnum.CERTIFICATE, FileSizeEnum.MEGABYTE, 1);

        TenantCertificate tenantCertificate = TenantCertificate.builder()
            .cloudCertificateId(cloudCertificateId)
            .name(generateCertificateName())
            .tenantCode(platformAppName)
            .expireTime(CertificateUtil.getExpireTime(certificate))
            .contentObsKey(certificateObsFileInfoDto.getObjectKey())
            .secretKeyObsKey(certificateKeyObsFileInfoDto.getObjectKey())
            .build();

        tenantCertificateBaseMapper.insert(tenantCertificate);

        return tenantCertificate.getId();
    }

    /**
     * 查询基本配置信息
     */
    private void commonConfig() {

        enterpriseProjectId = iam.getEnterpriseProjectId();

        projectId = iam.getProjectId();

        IamAccountInfo iamInfoDto = iamAccountInfoBaseMapper.getIamInfo();
        String saasSalt = getSaasSalt();
        String password = EncryptionUtil.decrypt(saasSalt, iamInfoDto.getPassword());
        iamInfoDto.setPassword(password);

        headers = IamApi.getIamHeader(iam.getEndpoint(), iamInfoDto.getDomainName(), iamInfoDto.getIamName(),
            iamInfoDto.getPassword(), projectId);
    }

    /**
     * 获取平台侧口令
     *
     * @return 口令
     */
    private String getSaasSalt() {
        SaltDto saltDto = workSecretKeyMapper.getPlatformSaltDto();
        return EncryptionUtil.decrypt(saltDto.getPwdSalt(), saltDto.getPassword());
    }

    /**
     * 更新租户应用域名
     *
     * @param tenantCertificateId 证书ID
     * @param wafHostResourceId WAF防护域名记录ID
     */
    private void updateTenantAppDomain(Long tenantCertificateId, Long wafHostResourceId) {
        AppTemplate saasAppInfo = appTemplateBaseMapper.getSaasAppInfo();
        TenantAppDomain tenantAppDomain =
            tenantAppDomainBaseMapper.getByTenantAndApp(platformAppName, saasAppInfo.getId());
        tenantAppDomain.setCertificateId(tenantCertificateId)
            .setWafHostResourceId(wafHostResourceId)
            .setStatus(TenantAppDomainStatusEnum.WAF_CONFIGURE_SUCCESS.getStatus());
        tenantAppDomainBaseMapper.updateById(tenantAppDomain);
    }
}
